package com.haidao.admin.component.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.db.handler.BeanListHandler;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import com.haidao.admin.component.dao.ReportRecordDao;
import com.haidao.admin.component.entity.ReportRecord;
import com.haidao.admin.component.params.ReportParam;
import com.haidao.admin.component.resp.PageResult;
import com.haidao.admin.component.service.ReportService;
import com.haidao.admin.component.service.TransactionService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Slf4j
public abstract class AbstractReportService<T> implements ReportService<T> {

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    private final DataSource dataSource = SpringUtil.getBean(DataSource.class);
    private final JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    private final ReportRecordDao reportRecordDao = SpringUtil.getBean(ReportRecordDao.class);
    private final TransactionService transactionService = SpringUtil.getBean(TransactionService.class);
    private final Map<String, String> uriMap;
    private String cmcID = null;

    public AbstractReportService() {
        uriMap = new HashMap<>();
        buildTableUriMap();
        cmcID = SpringUtil.getProperty("report.cmcId");
    }

    // 查询未上报的数据
    protected abstract List<T> selectList();

    protected abstract JSONObject buildRequestBody(T bean, String cmcID);

    protected abstract T selectById(int id);

    @Override
    public void report(String tableName, Integer id, String action) {
        T obj  = selectById(id);
        if (obj == null) {
            return;
        }
        log.info("查询到的数据" + obj);
        Timestamp startTime = new Timestamp(new Date().getTime());
        Integer reportState = reportToServer(tableName, obj, action);
        boolean state = reportState != null;   // 上报状态 true:成功 false:失败
        transactionService.transactional(() -> {
            //保证同时成功或者同时失败
            if (state) {
                updateReportState(tableName, id);
            }
            recordToDb(state, tableName, id, startTime, 1L);
        });
    }

    @Override
    public final void report(String tableName, long taskId) {
        List<T> list = selectList();
        if (list == null || list.isEmpty()) {
            return;
        }
        log.info("上报条数:{}", list.size());
        for (final T object : list) {
            executor.submit(new Runnable() {
                @Override
                public void run() {
                    Timestamp startTime = new Timestamp(new Date().getTime());
                    Integer id = reportToServer(tableName, object, "A");
                    JSONObject jsonObject = new JSONObject(object);
                    Integer id1 = jsonObject.getInt("id");
                    boolean state = id != null;   // 上报状态 true:成功 false:失败
                    transactionService.transactional(() -> {
                        // 保证同时成功或者同时失败
                        if (state) {
                            updateReportState(tableName, id1);
                        }
                        recordToDb(state, tableName, id1, startTime, taskId);
                    });
                }
            });
        }
    }

    @Override
    public final Page<T> selectReportRecord(ReportParam param) {
        String sql = buildSql(param);
        Integer totalCount = getTotalCount(sql);
        PageRequest request =PageRequest.of(param.getPageNumber(),param.getPageSize());
        sql += StrUtil.format(" limit {},{}", (param.getPageNumber() - 1) * param.getPageSize(), param.getPageSize());
        List<T> objects = jdbcTemplate.query(sql, rs -> {
            BeanListHandler<T> handler = BeanListHandler.create(getGenericTypeClass());
            return handler.handle(rs);
        });
        return new PageImpl<>(objects,request,totalCount);
    }

    public Class<T> getGenericTypeClass() {
        // 获取带有泛型参数的父类类型
        Type genericSuperclass = getClass().getGenericSuperclass();
        // 判断父类类型是否是参数化类型
        if (genericSuperclass instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
            // 获取实际的泛型参数类型数组
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            // 获取第一个泛型参数的Class对象
            if (actualTypeArguments.length > 0 && actualTypeArguments[0] instanceof Class) {
                return (Class<T>) actualTypeArguments[0];
            }
        }
        // 如果无法获取到泛型参数类型，可以抛出异常或者返回null，取决于具体需求
        throw new IllegalArgumentException("无法获取泛型参数类型");
    }



    /**
     * 查询总数
     * @param  sql  sql语句
     * @return 总数
     */
    private Integer getTotalCount(String sql) {
        String sqlForCount = "select count(*) from ( " + sql + " ) total";
        return jdbcTemplate.queryForObject(sqlForCount, Integer.class);
    }

    /**
     * 构建查询sql
     * @param reportParam 查询参数
     * @return sql语句
     */
    private String buildSql(ReportParam reportParam) {
        String tableName = reportParam.getTableName();
        String sql = StrUtil.format("""
                select
                    a.*, b.*
                from
                    {} b
                left join
                    report_record a
                on
                    a.res_id = b.id
                where a.task_key='{}'    
                """, tableName, tableName);
        if (reportParam.getReportState() == 3) {
            sql += " and a.report_id is null";
            return sql;
        }
        if (reportParam.getReportState() != 2) {
            sql += " and b.report_state = " + reportParam.getReportState();
        }
        return sql;
    }

    /**
     * 更新上报状态
     * @param tableName 表名
     * @param id       对象id
     */
    private void updateReportState(String tableName, Integer id) {
        if (id == null) {
            return;
        }
        String sql = "update " + tableName + " set report_state = 1 where id = " + id;
        jdbcTemplate.execute(sql);
    }

    /**
     * 记录上报记录
     *
     * @param success   是否上报成功
     * @param tableName 表名
     * @param id        对象id
     */
    private void recordToDb(boolean success, String tableName, Integer id, Timestamp startTime, long taskId) {
        ReportRecord reportRecord = new ReportRecord();
        reportRecord.setResId(id);
        reportRecord.setTaskKey(tableName);
        reportRecord.setReportCount(1);
        reportRecord.setSuccessCount(success ? 1 : 0);
        reportRecord.setFailCount(!success ? 1 : 0);
        reportRecord.setStartTime(startTime);
        reportRecord.setEndTime(new Timestamp(new Date().getTime()));
        reportRecord.setTaskId(taskId);
        reportRecordDao.save(reportRecord);
    }


    /**
     * 上报到服务器,返回对象id
     *
     * @param tableName 表名
     * @param object    上报对象
     * @return 返回对象id
     */
    private Integer reportToServer(String tableName, T object, String reportType) {
        JSONObject reqBody = buildRequestBody(object, cmcID);
        String uri = uriMap.get(tableName);
        String baseUrl = SpringUtil.getProperty("report.baseUrl");
        String url = baseUrl + uri;

        try {
            log.info("上报url:{},上报数据:{}", url, reqBody);
            String post = HttpUtil.post(url, reqBody.toString());
            JSONObject resp = new JSONObject(post);
            log.info("返回结果:{}", resp);
            if (resp.getInt("code") == null || resp.getInt("code") != 0) {
                log.error("上报失败:{}", resp.getStr("msg"));
                return null;
            }
            Integer id = reqBody.getInt("id");
            if (id == null) {
                id = reqBody.getJSONArray("data").getJSONObject(0).getInt("id");
            }
            return id;
        }catch (Exception e) {
            log.error("调用接口{}失败",url);
        }
        return null;
    }

    /**
     * 构建表名和uri的映射关系
     */
    private void buildTableUriMap() {
        // 2.1.1平台注册
        uriMap.put("report_register_info", "/mpscmc/v1/subcmc/register");
        // 2.1.2建设状态
        uriMap.put("report_construct_status", "/mpscmc/v1/subcmc/construct_status");
        // 2.1.3子平台建设情况
        uriMap.put("report_platform_info", "/mpscmc/v1/subcmc/platform_info");
        // 2.1.4拓扑图
        uriMap.put("report_network_topology", "/mpscmc/v1/subcmc/network_topology");
        // 2.2.1机构基本信息
        uriMap.put("report_org_info", "/mpscmc/v1/ua/org");
        // 2.2.2用户基本信息
        uriMap.put("report_user_info", "/mpscmc/v1/ua/user");
        // 2.2.3用户活跃信息
        uriMap.put("report_user_daus", "/mpscmc/v1/ua/user/daus");
        // 2.3.1移动终端设备基本信息
        uriMap.put("report_device_info", "/mpscmc/v1/mdm/device");
        // 2.3.2移动终端设备状态信息
        uriMap.put("report_device_stat", "/mpscmc/v1/mdm/device/stat");
        // 2.3.3接入网络基本信息
        uriMap.put("report_network_info", "/mpscmc/v1/infr/network");
        // 2.3.4网络设施
        uriMap.put("report_hardware_info", "/mpscmc/v1/hardware");
        // 2.4.1应用支撑系统信息
        uriMap.put("report_system_info", "/mpscmc/v1/appspt/system");
        // 2.5.1移动应用APP信息
        uriMap.put("report_app_info", "/mpscmc/v1/pm/app");
        // 2.5.2移动应用服务端信息
        uriMap.put("report_app_service", "/mpscmc/v1/pm/service");
        // 2.5.3移动应用状态信息
        uriMap.put("report_app_stat", "/mpscmc/v1/pm/app/stat");
        // 2.5.4移动应用整体日活跃信息
        uriMap.put("report_app_platform_daus", "/mpscmc/v1/pm/app/platform_daus");
        // 2.5.5移动应用个体日活跃信息
        uriMap.put("report_app_daus", "/mpscmc/v1/pm/app/daus");
        // 2.6.1服务资源信息
        uriMap.put("report_resservice_info", "/mpscmc/v1/mr/resservice");
        // 2.6.2服务资源状态信息
        uriMap.put("report_resservice_stat", "/mpscmc/v1/mr/resservice/stat");
        // 2.6.3资源服务整体日活跃信息
        uriMap.put("report_resservice_platform_daus", "/mpscmc/v1/mr/resservice/platform_daus");
        // 2.6.4资源服务个体日活跃信息
        uriMap.put("report_resservice_daus", "/mpscmc/v1/mr/resservice/daus");
        // 2.7.1安全事件信息
        uriMap.put("report_security_event", "/mpscmc/v1/security/event");
    }

}
